#include <defs.h>
#include <x86.h>
#include <picirq.h>

// I/O Addresses of the two programmable interrupt controllers
#define IO_PIC1             0x20    // Master (IRQs 0-7)
#define IO_PIC2             0xA0    // Slave (IRQs 8-15)

#define IRQ_SLAVE           2       // IRQ at which slave connects to master

// Current IRQ mask.
// Initial IRQ mask has interrupt 2 enabled (for slave 8259A).
static uint16_t irq_mask = 0xFFFF & ~(1 << IRQ_SLAVE);
static bool did_init = 0;
/**
 * 设置中断请求(IRQ)掩码的函数
 * 此函数用于更新内部的IRQ掩码，并在设备初始化后，将掩码值写入到两个级联的可编程中断控制器(PIC)中
 * 
 * @param mask 一个16位的掩码值，用于配置IRQ线的掩码
 */
static void
pic_setmask(uint16_t mask) {
    // 更新内部的IRQ掩码
    irq_mask = mask;
    // 仅在设备已初始化的情况下执行后续操作
    if (did_init) {
        // 向主PIC发送低8位的掩码
        outb(IO_PIC1 + 1, mask);
        // 向从PIC发送高8位的掩码
        outb(IO_PIC2 + 1, mask >> 8);
    }
}
/**
 * 启用指定的中断源
 * 此函数通过更新中断掩码寄存器来启用特定的中断源
 * 
 * @param irq 要启用的中断源的编号
 */
void
pic_enable(unsigned int irq) {
    // 更新中断掩码，清除对应IRQ的掩码位以启用中断
    pic_setmask(irq_mask & ~(1 << irq));
}

/* pic_init - initialize the 8259A interrupt controllers */
// 初始化可编程中断控制器（PIC）
// 此函数配置 PIC 以准备处理系统中的中断
void
pic_init(void) {
    // 标记 PIC 初始化完成
    did_init = 1;

    // mask all interrupts
    // 屏蔽所有中断
    // 初始时，屏蔽所有中断以防止在设置过程中发生意外中断
    outb(IO_PIC1 + 1, 0xFF);
    outb(IO_PIC2 + 1, 0xFF);

    // Set up master (8259A-1)
    // 配置主 PIC（8259A-1）
    // 配置主 PIC 芯片

    // ICW1:  0001g0hi
    //    g:  0 = edge triggering, 1 = level triggering
    //    h:  0 = cascaded PICs, 1 = master only
    //    i:  0 = no ICW4, 1 = ICW4 required
    // ICW1:  0001g0hi
    //    g:  0 = 边沿触发，1 = 电平触发
    //    h:  0 = 级联 PIC，1 = 仅主 PIC
    //    i:  0 = 不需要 ICW4，1 = 需要 ICW4
    outb(IO_PIC1, 0x11);

    // ICW2:  中断向量偏移
    // ICW2:  Vector offset
    outb(IO_PIC1 + 1, IRQ_OFFSET);

    // ICW3:  (master PIC) bit mask of IR lines connected to slaves
    //        (slave PIC) 3-bit # of slave's connection to master
    // ICW3:  （主 PIC）IR 线连接到从 PIC 的位掩码
    //        （从 PIC）从 PIC 连接到主 PIC 的 3 位编号
    outb(IO_PIC1 + 1, 1 << IRQ_SLAVE);

    // ICW4:  000nbmap
    //    n:  1 = special fully nested mode
    //    b:  1 = buffered mode
    //    m:  0 = slave PIC, 1 = master PIC
    //        (ignored when b is 0, as the master/slave role
    //         can be hardwired).
    //    a:  1 = Automatic EOI mode
    //    p:  0 = MCS-80/85 mode, 1 = intel x86 mode
    // ICW4:  000nbmap
    //    n:  1 = 特殊全嵌套模式
    //    b:  1 = 缓冲模式
    //    m:  0 = 从 PIC，1 = 主 PIC
    //        （当 b 为 0 时忽略，因为主/从角色可以硬连线）
    //    a:  1 = 自动 EOI 模式
    //    p:  0 = MCS-80/85 模式，1 = Intel x86 模式
    outb(IO_PIC1 + 1, 0x3);

    // Set up slave (8259A-2)
    // 配置从 PIC（8259A-2）
    outb(IO_PIC2, 0x11);    // ICW1
    outb(IO_PIC2 + 1, IRQ_OFFSET + 8);  // ICW2
    outb(IO_PIC2 + 1, IRQ_SLAVE);       // ICW3
    // NB Automatic EOI mode doesn't tend to work on the slave.
    // Linux source code says it's "to be investigated".
    // 注意：自动 EOI 模式通常在从 PIC 上不起作用
    // Linux 源代码中提到这是“待调查”的问题
    outb(IO_PIC2 + 1, 0x3);             // ICW4

    // OCW3:  0ef01prs
    //   ef:  0x = NOP, 10 = clear specific mask, 11 = set specific mask
    //    p:  0 = no polling, 1 = polling mode
    //   rs:  0x = NOP, 10 = read IRR, 11 = read ISR
    // OCW3:  0ef01prs
    //   ef:  0x = 无操作，10 = 清除特定掩码，11 = 设置特定掩码
    //    p:  0 = 无轮询，1 = 轮询模式
    //   rs:  0x = 无操作，10 = 读取 IRR，11 = 读取 ISR
    outb(IO_PIC1, 0x68);    // clear specific mask// 清除特定掩码
    outb(IO_PIC1, 0x0a);    // read IRR by default// 默认读取 IRR

    outb(IO_PIC2, 0x68);    // OCW3
    outb(IO_PIC2, 0x0a);    // OCW3
    
    // 如果有自定义的中断掩码，则设置中断掩码
    if (irq_mask != 0xFFFF) {
        pic_setmask(irq_mask);
    }
}

